package com.dc.commons.shiro.filter;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dc.commons.helper.JsonHelper;
import com.dc.commons.json.JsonWrapper;
import com.dc.commons.servlet.Servlets;
import com.dc.commons.shiro.session.DcSession;
/**
 * *****************************************************************************
 * 文件名: DcFormAuthenticationFilter.java <br>
 * 版本: 1.0<br>
 * 描述: FormAuthenticationFilter 扩展<br>	
 * 		重写 是否验证通过方法，添加了用户是否被踢的验证<br>
 * 		增加了登录判断中，如果是登录的url 但是用户已经登录，直接跳转到成功页面即可。<br>
 * 		将失败的信息进行保存，便于提取信息。<br>
 * 		登录成功后 如果是 ajax请求 则直接返回 json数据即可<br>
 *  	为了保证 Json 登录 将 匹配登录的url /main/login.htm  改为匹配的url为 main/login.* 【正常登录 login.htm json登录 login.json】 <br>
 * 版权所有: <br>
 * <P>
 * 创建者: 王建栋 <br>
 * 创建日期: 2015年12月14日 下午10:44:26<br>
 * 修改者: <br>
 * 修改日期: <br>
 * 修改说明: <br>
 *****************************************************************************
 */
public class DcFormAuthenticationFilter extends FormAuthenticationFilter {
	
	private static final Logger log = LoggerFactory.getLogger(DcFormAuthenticationFilter.class);
	
	/**
	 * Session Dao
	 */
	private SessionDAO sessionDao;
	

	public void setSessionDao(SessionDAO sessionDao) {
		this.sessionDao = sessionDao;
	}

	public DcFormAuthenticationFilter(){
		this.setUsernameParam("userCode");
		this.setPasswordParam("userPassword");
	}
	 
	/**
	 * 重写 是否验证通过方法，添加了用户是否被踢的验证
	 */
	@Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return super.isAccessAllowed(request, response, mappedValue)&&!isForced(request,response)&&!isLoginRequest(request, response);
    }
	 
	 /**
	  * 判断用户是否已经被踢下 失效
	  * @param request
	  * @param response
	  * @return isForced
	  */
	 private boolean isForced(ServletRequest request, ServletResponse response){
		 Subject subject = getSubject(request, response);
		 DcSession dcSession=(DcSession)sessionDao.readSession(subject.getSession().getId());
		 if(!dcSession.isDcValid()){
			 return true;
		 }
		 return false;
	 }
	 

	
	 /**
	  * 增加了登录判断中，如果是登录的url 但是用户已经登录，直接跳转到成功页面即可。
	  */
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
            	 Subject subject = getSubject(request, response);
            	 if(subject.isAuthenticated()){
            		 if (log.isTraceEnabled()) {
            			 log.trace(" user isAuthenticated redirectToSuccess.");
            		 }
            		 redirectToSuccess(request,response);
            		 return false;
            	 }else{
            		 if (log.isTraceEnabled()) {
            			 log.trace("Login page view.");
            		 }
            		 //allow them to see the login page ;)
            		 return true;
            	 }
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }

            saveRequestAndRedirectToLogin(request, response);
            return false;
        }
    }
    
    /**
     * 跳转到登录页面
     * @param request
     * @param response
     * @throws IOException
     */
    protected void redirectToSuccess(ServletRequest request, ServletResponse response) throws IOException {
        String successUrl = getSuccessUrl();
        WebUtils.redirectToSavedRequest(request, response, successUrl);
    }
    
    /**
     * 将失败的信息进行保存，便于提取信息。
     */
    protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
    	super.setFailureAttribute(request, ae);
        request.setAttribute(getFailureKeyAttribute()+".MESSAGE", ae.getMessage());
    }

	
    /**
     * 登录成功后 如果是 ajax请求 则直接返回 json数据即可。
     */
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
			ServletResponse response) throws Exception {
		if(Servlets.isAjax(request)){
			response.setCharacterEncoding("UTF-8");
			response.setContentType("application/json");
	        PrintWriter out = response.getWriter();
	        out.println(JsonHelper.fromObjectToJson(JsonWrapper.success().setResult(getSuccessUrl())));
	        out.flush();
	        out.close();
		}else{
			super.issueSuccessRedirect(request, response);
		}
		return false;
	}
    
	/**
	 *  由于 login.htm是包含有页面的导致 如果将 login链接直接修改为login.json 将导致无法跳转到登录页面。
	 *  所以针对 json登录  使用  将原来的 login.htm的配 转换为 login.*的匹配 可以使用 login.htm，login.json 同时进行登录
	 */
	 @Override
	 protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
	        return pathsMatch(loginMatcherUrl, request);
	 }
	 
	 /**
	  * 将匹配登录的url保存下来 /main/login.htm  匹配的url为 main/login.*
	  */
	 public void setLoginUrl(String loginUrl) {
	       super.setLoginUrl(loginUrl);
	       loginMatcherUrl=loginUrl;
	       if(loginMatcherUrl.indexOf(".")!=-1){
	    	   loginMatcherUrl=loginMatcherUrl.substring(0,loginMatcherUrl.indexOf("."))+".*";
	       }
	   }
	 
	private String loginMatcherUrl;
}
